package cn.dnui_xcz826.dnuifood_16110100826.controller;

import android.animation.ObjectAnimator;
import android.animation.ValueAnimator;
import android.content.Context;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.view.animation.Interpolator;
import android.widget.Scroller;

/**
 * Created by Administrator on 2018/11/29.
 */

public class SwipeView extends ViewGroup {

    private int downX, moveX;
    private boolean isShowRight;
    private static SwipeView sView;
    private static final int MAX_BOUNDARY = 10;
    private int deleteWidth;

    private Scroller scroller = new Scroller(getContext());//初始化滚动器, 数值模拟器

    public SwipeView(Context context) {
        super(context);
    }

    public SwipeView(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    public SwipeView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        //测量控件宽高,在该view的父控件测量自己宽高时调用。因为该view所在父容器在测量自己宽高时，会先测量子view的宽高，最终都会调用child.measure(w,h)，最后才测量自己的宽高。
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        measureChildren(widthMeasureSpec, heightMeasureSpec);
        int width = MeasureSpec.getSize(widthMeasureSpec);
        View child = getChildAt(0);//获得当前能看到的item的第一个
        int margin = ((MarginLayoutParams) child.getLayoutParams()).topMargin//获取控件的LayoutParams,LayoutParams相当于一个Layout的信息包，它封装了Layout的位置、高、宽等信息。
                + ((MarginLayoutParams) child.getLayoutParams()).bottomMargin;
        setMeasuredDimension(width, getChildAt(0).getMeasuredHeight() + margin);
    }

    @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {
        getChildAt(0).layout(l, t, r, b);
        //getChildAt(int position)方法的参数position指的是当前可见区域中的ChildView位置
        View view = getChildAt(1);
        view.layout(r, t, r + view.getMeasuredWidth(), b);//删除按钮排布在第一个View之后
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        if(!scroller.isFinished()){
            return false;
        }
        switch (event.getAction()) {
            case MotionEvent.ACTION_MOVE://当触点在屏幕上移动时触发
                moveX = (int) event.getRawX();//相对屏幕位置
                int moved = moveX - downX;
                if (isShowRight) {
                    moved -= deleteWidth;//如果已经展示出删除按钮，移动距离+删除按钮的宽度
                }
                if (-moved <= deleteWidth) {
                    scrollTo(-moved, 0);
                } else {
                    int delta = -moved - deleteWidth;
                    int percent = (int) ((deleteWidth - delta / 5) / (float) deleteWidth * delta);
                    View view = getChildAt(1);
                    LayoutParams layoutParams = view.getLayoutParams();
                    layoutParams.width = deleteWidth + percent;//随着向左划增加删除按钮的宽度
                    view.setLayoutParams(layoutParams);
                    scrollTo(deleteWidth + percent, 0);
                }
                if (getScrollX() <= 0) {
                    scrollTo(0, 0);
                }
                break;
            case MotionEvent.ACTION_CANCEL://不是由用户直接触发，由系统在需要的时候触发
            case MotionEvent.ACTION_UP://当屏幕上有多个点被按住，松开其中一个点时触发
                if (getScrollX() >= deleteWidth / 2) {
                    isShowRight = true;
                    sView = this;
                    recoverAnimation();
                    smoothScrollToPosition(deleteWidth);
                } else {
                    isShowRight = false;
                    smoothScrollToPosition(0);
                }
                break;
            default:
                break;
        }
        return true;
    }

    private void smoothScrollToPosition(int destX) {//利用scroller实现弹性滑动
        int width = getScrollX();//view的左边缘在坐标系中的位置
        int delta = destX - width;
        scroller.startScroll(width, 0, delta, 0, 500);//提供的起始点和将要滑动的距离开始滚动。public void startScroll (int startX, int startY, int dx, int dy)
        invalidate();
    }

    @Override
    public void computeScroll() {
        if (scroller.computeScrollOffset()) {//判断滚动操作是否已经完成
            scrollTo(scroller.getCurrX(), scroller.getCurrY());//scrollTo(x, y)标识移动到一个具体的坐标点(x, y)
            postInvalidate();//刷新界面
        }
    }

    @Override
    public LayoutParams generateLayoutParams(AttributeSet attrs) {//获取默认的LayoutParams属性
        return new MarginLayoutParams(getContext(), attrs);
    }

    @Override
    protected void onDetachedFromWindow() {//销毁View的时候。写的这个View不再显示
        super.onDetachedFromWindow();
        if (sView != null) {
            sView.close();
        }
    }

    private void close() {
        smoothScrollToPosition(0);//平滑到想显示的项
        isShowRight = false;
    }

    public static boolean closeMenu(View v) {
        boolean flag = false;
        if (sView != null) {
            sView.close();
            flag = sView.getChildAt(0) == v || sView.getChildAt(1) == v;
            sView = null;
        }
        return flag;
    }

    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {//用于处理事件并改变事件的传递方向
        switch (ev.getAction()) {
            case MotionEvent.ACTION_DOWN://当屏幕检测到第一个触点按下之后
                downX = (int) ev.getRawX();//触摸点相对于屏幕的坐标
                if (sView != null && sView != this) {
                    sView.close();
                }
                deleteWidth = getChildAt(1).getWidth();
                break;
            case MotionEvent.ACTION_MOVE:
                moveX = (int) ev.getRawX();
                if (Math.abs(moveX - downX) > MAX_BOUNDARY) {
                    getParent().requestDisallowInterceptTouchEvent(true);//在左划时拦截事件，防止左划同时还能上下滑动
                    return true;
                }
                break;
            default:
                break;
        }
        return super.onInterceptTouchEvent(ev);
    }

    private void recoverAnimation() {//在手抬起时需要恢复删除按钮的宽度
        final View view = getChildAt(1);
        final SwipeLayoutParams swipeLayoutParams = new SwipeLayoutParams(view.getLayoutParams());
        ObjectAnimator animator = ObjectAnimator.ofInt(swipeLayoutParams, "width", deleteWidth).setDuration(500);
        animator.setInterpolator(new ViscousFluidInterpolator());
        animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                view.setLayoutParams(swipeLayoutParams);
            }
        });
        animator.start();
    }

    static class ViscousFluidInterpolator implements Interpolator {//设置动画速度
        /** Controls the viscous fluid effect (how much of it). */
        private static final float VISCOUS_FLUID_SCALE = 8.0f;

        private static final float VISCOUS_FLUID_NORMALIZE;
        private static final float VISCOUS_FLUID_OFFSET;

        static {

            // must be set to 1.0 (used in viscousFluid())
            VISCOUS_FLUID_NORMALIZE = 1.0f / viscousFluid(1.0f);
            // account for very small floating-point error
            VISCOUS_FLUID_OFFSET = 1.0f - VISCOUS_FLUID_NORMALIZE * viscousFluid(1.0f);
        }

        private static float viscousFluid(float x) {
            x *= VISCOUS_FLUID_SCALE;
            if (x < 1.0f) {
                x -= (1.0f - (float)Math.exp(-x));
            } else {
                float start = 0.36787944117f;   // 1/e == exp(-1)
                x = 1.0f - (float)Math.exp(1.0f - x);
                x = start + x * (1.0f - start);
            }
            return x;
        }

        @Override
        public float getInterpolation(float input) {//input参数是系统根据设置的动画持续时间计算出来的，取值范围是[0,1]，从0匀速增加到1，
            final float interpolated = VISCOUS_FLUID_NORMALIZE * viscousFluid(input);
            if (interpolated > 0) {
                return interpolated + VISCOUS_FLUID_OFFSET;
            }
            return interpolated;
        }
    }

    private static class SwipeLayoutParams extends LayoutParams{
        public SwipeLayoutParams(LayoutParams source) {
            super(source);
        }

        public void setWidth(int newWidth) {
            width = newWidth;
        }

        public int getWidth() {
            return width;
        }
    }
}
